﻿//////////////////////////////////////////////////////////////////
//
// Main.cs
//
// Copyright (c) 2012 Dan Pike.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using Interfaces;
using Network;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Utilities;
using System.Reflection;
using System.Threading;
using System.Diagnostics;

namespace Camilla
{
   public partial class Main : Form
   {
      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      private static Logger logger_;
      private NetworkClient network_;
      private IPlanet planet_;

      // Instantiate the dash-board manually rather than using the designer because
      // it may crash during development!
      private IDashboard dashboard_;

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      public Main()
      {
         logger_ = Program.Logger;
         InitializeComponent();
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Received an IPlanetOnline message
      /// </summary>
      void HandlePlanetOnline(object sender, PlanetOnlineEventArgs eventArgs)
      {
         try
         {
            IPlanetOnline planetOnline = eventArgs.PlanetOnline;

            if (!planetOnline.Online)
            {
               // Stop showing the current planet
               planet_ = null;
               dashboard_.Planet = null;
               network_.Planet = null;
            }
            else
            {
               // Show the planet
               IPlanetFact planetFact = Program.Almanac.Planets
                  .First(planet => planet.DisplayName == planetOnline.PlanetName);
               planet_ = Program.Loader.Create<IPlanet>();
               if (planet_.Initialize(planetFact, planetOnline.PlanetName))
               {
                  // Show the planet
                  dashboard_.Planet = planet_;
                  network_.Planet = planet_;

                  // and set the home location on it
                  ILocation position = planetOnline.StartLocation;
                  ILocation orientation = planetOnline.Orientation;
                  if ((null == position) || (null == orientation))
                  {
                     position = planet_.Fact.HomeAddress.Clone();
                     position.Move(0, 0, 50);
                     orientation = position.Clone().Move(10, 10, -5);
                  }
                  dashboard_.SetCamera(position, orientation, true);
               }
            }
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="eventArgs"></param>
      void handleSenderChanged(object sender, NetworkGui.SenderChangedEventArgs eventArgs)
      {
         try
         {
            if (Identifiers.Charles == eventArgs.senderId)
            {
               SafeAsyncInvoker.BeginInvoke(statusBar, () =>
                  {
                     statusCharles.Text = string.Concat("server: ", eventArgs.statusText);
                     statusCharles.BackColor = eventArgs.isAlive ? SystemColors.Control : Color.Red;
                  });
            }
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void menuExit_Click(object sender, EventArgs e)
      {
         try
         {
            Close();
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Main_Load(object sender, EventArgs e)
      {
         try
         {
            dashboard_ = Program.Loader.Create<IDashboard>();
            dashboard_.Name = "dashBoard";
            dashboard_.Dock = DockStyle.Fill;
            panelDashboard.Controls.Add(dashboard_.AsControl);
            loggerViewer.Dock = DockStyle.Fill;
            Text = string.Concat(Application.ProductName);

            // The network class also manages various GUI elements, as those
            // may be common to lots of the applications
            NetworkGui.GuiObjects gui = new NetworkGui.GuiObjects();
            gui.hostForm = this;
            gui.menuLogin = menuLogin;
            gui.menuLogout = menuLogout;
            gui.statusBar = statusBar;
            gui.statusNetwork = statusNetwork;
            gui.statusText = statusText;
            gui.statusHeartbeat = statusHeartbeat;
            network_ = new NetworkClient(Program.SenderId, gui);
            network_.EventSenderChanged += new NetworkGui.SenderChangedHandler(handleSenderChanged);
            network_.EventPlanetOnline += new PlanetOnlineHandler(HandlePlanetOnline);

            // Now that we have started up, we can initialize all of the scripts
            // to define the behaviour of all of the entities in the game
            if (!Program.Almanac.Scripts.All(scriptFile => scriptFile.Initialize()))
            {
               logger_.ErrorFormat("Failed to initialize the scripts");
               Close();
            }
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void newGameToolStripMenuItem_Click(object sender, EventArgs e)
      {
         try
         {
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// If Charles is not running, prompt to see if the user wants to start
      /// it
      /// </summary>
      private void checkCharles(bool checkStarted)
      {
         bool createdNew = false;
         using (EventWaitHandle charlesRunning = new EventWaitHandle(false,
            EventResetMode.ManualReset, Identifiers.Charles, out createdNew))
         {
            if (checkStarted)
            {
               if (!createdNew)
               {
                  // Already started
                  return;
               }
               StartStopOptions startup = Program.Settings.StartCharles;
               bool start = false;
               switch (startup)
               {
                  case StartStopOptions.Always:
                     start = true;
                     break;

                  case StartStopOptions.Prompt:
                     start = (DialogResult.Yes == MessageBox.Show(this,
                        "Do you want to start Charles, too?", Application.ProductName, MessageBoxButtons.YesNo));
                     break;

                  default:
                     break;
               }
               if (start)
               {
                  logger_.DebugFormat("Starting Charles, too");
                  Process.Start("Charles.exe");
               }
            }
            else
            {
               if (!createdNew)
               {
                  bool stop = false;
                  switch (Program.Settings.StopCharles.Value)
                  {
                     case StartStopOptions.Always:
                        stop = true;
                        break;

                     case StartStopOptions.Prompt:
                        stop = (DialogResult.Yes == MessageBox.Show(this,
                           "Do you want to stop Charles, too?", Application.ProductName, MessageBoxButtons.YesNo));
                        break;

                     default:
                        break;
                  }
                  if (stop)
                  {
                     logger_.DebugFormat("signalling Charles to stop");
                     charlesRunning.Set();
                  }
               }
            }
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Main_Shown(object sender, EventArgs e)
      {
         try
         {
            // Start Charles if it's not already started
            checkCharles(true);

            // Get a network connection
            if (!network_.Initialize())
            {
               // This is pretty fatal :(
               MessageBox.Show(string.Concat(Application.ProductName, " failed to initialize network connection"),
                  Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
               Close();
               return;
            }

            // Start with an automatic login
            network_.Login(Program.Settings.AutoLogin);

            // Enable the housekeeping
            guiTimer.Enabled = true;

            // start with a new game by default
            network_.openPlanet("Earth");
            //newGameToolStripMenuItem_Click(null, null);
            //dashboard_.isActive = true;
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Main_FormClosing(object sender, FormClosingEventArgs e)
      {
         try
         {
            if (null != network_)
            {
               network_.closePlanet();

               network_.Shutdown();
               network_ = null;
            }

            // Disconnect the display
            dashboard_.Planet = null;

            // Should we stop the server?
            checkCharles(false);

            // disconnect the debug log from our list view
            loggerViewer.Shutdown();
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void handleTick(object sender, EventArgs e)
      {
         try
         {
            // Should we shut down?
            if (Program.ProgramRunning.WaitOne(0))
            {
               logger_.DebugFormat("detected remote request to shut down");
               Close();
               return;
            }

            // Snapshot the time, so we use a consistent value throughout this method
            DateTime now = DateTime.Now;

            // Tell the network that we are still alive
            network_.Heartbeat(now);

            // And check to see if the dashboard wants to do anything
            dashboard_.Tick();
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void handleOptionMenuItem_Click(object sender, EventArgs e)
      {
         try
         {
            if (wireframeToolStripMenuItem == sender)
            {
               // Toggle the wire-frame display
               dashboard_.Wireframe = !dashboard_.Wireframe;
            }
            if (lightingToolStripMenuItem == sender)
            {
               // Toggle the lights
               dashboard_.Lighting = !dashboard_.Lighting;
            }
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void handleOptions_DropDownOpening(object sender, EventArgs e)
      {
         try
         {
            wireframeToolStripMenuItem.Checked = dashboard_.Wireframe;
            lightingToolStripMenuItem.Checked = dashboard_.Lighting;
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void charlesShutdownToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
      {
         charlesShutdownAlwaysToolStripMenuItem.Checked =
            charlesShutdownNeverToolStripMenuItem.Checked =
            charlesShutdownPromptToolStripMenuItem.Checked = false;
         switch (Program.Settings.StopCharles.Value)
         {
            case StartStopOptions.Always:
               charlesShutdownAlwaysToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.Never:
               charlesShutdownNeverToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.Prompt:
               charlesShutdownPromptToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.None:
            default:
               break;
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void charlesStartupToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
      {
         charlesStartupAlwaysToolStripMenuItem.Checked =
            charlesStartupNeverToolStripMenuItem.Checked =
            charlesStartupPromptToolStripMenuItem.Checked = false;
         switch (Program.Settings.StartCharles.Value)
         {
            case StartStopOptions.Always:
               charlesStartupAlwaysToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.Never:
               charlesStartupNeverToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.Prompt:
               charlesStartupPromptToolStripMenuItem.Checked = true;
               break;

            case StartStopOptions.None:
            default:
               break;
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void charlesShutdown_Click(object sender, EventArgs e)
      {
         Program.Settings.StopCharles.Value =
            (charlesShutdownAlwaysToolStripMenuItem == sender) ? StartStopOptions.Always :
            (charlesShutdownNeverToolStripMenuItem == sender) ? StartStopOptions.Never :
            (charlesShutdownPromptToolStripMenuItem == sender) ? StartStopOptions.Prompt :
            StartStopOptions.None;
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// 
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void charlesStartup_Click(object sender, EventArgs e)
      {
         Program.Settings.StartCharles.Value =
            (charlesStartupAlwaysToolStripMenuItem == sender) ? StartStopOptions.Always :
            (charlesStartupNeverToolStripMenuItem == sender) ? StartStopOptions.Never :
            (charlesStartupPromptToolStripMenuItem == sender) ? StartStopOptions.Prompt :
            StartStopOptions.None;
      }
   }
}
